HEX
Server: Apache/2.4.68 (Debian)
System: Linux as-cs-widget-demo-us-central1 6.1.0-44-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.164-1 (2026-03-09) x86_64
User: root (0)
PHP: 8.2.32
Disabled: NONE
Upload Files
File: /var/www/kevin-demo/wp-content/plugins/allspice/includes/membership-ads.php
<?php
// includes/membership-ads.php
//
// Ad suppression for ad-free members, done server-side: PHP knows access before the page
// renders (signed cookie, includes/membership.php), so we emit what the ad networks
// actually consume:
//  - Raptive/AdThrive: the `adthrive-disable-all` body class (their supported site-level
//    switch; the old `window.adthriveDisableAds` global is not used)
//  - Mediavine: a `#mediavine-settings` element with per-slot data-blocklist attributes
// Both are emitted with no provider detection. Each signal is inert on the other
// network's sites, so sniffing the installed network would only add a failure mode.
//
// Everything here is gated on the `adFree` benefit from a validated member session.
// Anonymous readers, non-members, members without that benefit, and invalid/expired
// cookies produce no output. Membership alone is not ad-free; the publisher has to
// enable the benefit.

if (!defined('ABSPATH')) exit;

/*
 * Single gate for this module: a validated member session carrying the `adFree` benefit.
 * `adFree` (camelCase) is the canonical id, the key in ALLSPICE_BUILTIN_BENEFITS and what
 * the signed cookie stores. This once checked `ad_free`, which the cookie never contains,
 * so ad-free members kept seeing ads. Compare against the stored id only.
 */
function allspice_member_is_ad_free(): bool {
    return function_exists('allspice_member_has_benefit') && allspice_member_has_benefit('adFree');
}

/*
 * Emergency per-network kill switch, not provider detection. Both default on; the filter
 * exists so a broken interaction with one network can be turned off without a release.
 */
function allspice_ad_free_networks(): array {
    $defaults = ['raptive' => true, 'mediavine' => true];
    $networks = apply_filters('allspice_ad_free_networks', $defaults);
    return is_array($networks) ? $networks : $defaults;
}

/*
 * Body classes, server-side. `adthrive-disable-all` is Raptive's own switch; the two
 * allspice-* classes are ours (styling hooks + the page bundle's "server-visible state"
 * marker for its guarded one-time reload).
 */
add_filter('body_class', 'allspice_member_body_classes', 1);
function allspice_member_body_classes(array $classes): array {
    if (!allspice_member_is_ad_free()) return $classes;
    if (!empty(allspice_ad_free_networks()['raptive'])) {
        $classes[] = 'adthrive-disable-all';
        /* Raptive treats VIDEO as its own product (live finding, baking4happiness
           2026-08-26): disable-all removes display units only, and their plugin gates the
           Playlist + Sticky Outstream players on this separate class (it also feeds
           window.adthriveCLS.videoDisabledFromPlugin from it). Without it, an ad-free
           member lost the banners but kept the sponsored video in the post and the sticky
           corner player. Auto-INSERTED players only: a video the publisher embedded by
           hand is content, not an ad, and stays. */
        $classes[] = 'adthrive-disable-video';
    }
    $classes[] = 'allspice-paid-member';
    $classes[] = 'allspice-ad-free-member';
    return $classes;
}

/*
 * The Mediavine per-slot blocklist for ad-free members: every display slot Mediavine
 * documents for `#mediavine-settings` is blocked. Universal-player attributes are held
 * behind a filter until the intended video-player behavior is verified - flipping
 * `allspice_mediavine_blocklist_universal_player` to true adds them, no release needed.
 */
function allspice_mediavine_settings_attributes(): array {
    $attrs = [
        'data-blocklist-leaderboard' => '1',
        'data-blocklist-sidebar-atf' => '1',
        'data-blocklist-sidebar-btf' => '1',
        'data-blocklist-content-desktop' => '1',
        'data-blocklist-content-mobile' => '1',
        'data-blocklist-adhesion-mobile' => '1',
        'data-blocklist-adhesion-tablet' => '1',
        'data-blocklist-adhesion-desktop' => '1',
        'data-blocklist-recipe' => '1',
        'data-blocklist-auto-insert-sticky' => '1',
        'data-blocklist-chicory' => '1',
        'data-blocklist-zergnet' => '1',
        'data-blocklist-interstitial-mobile' => '1',
        'data-blocklist-interstitial-desktop' => '1',
    ];
    if (apply_filters('allspice_mediavine_blocklist_universal_player', false)) {
        $attrs['data-blocklist-universal-player-desktop'] = '1';
        $attrs['data-blocklist-universal-player-mobile'] = '1';
    }
    return apply_filters('allspice_mediavine_settings_attributes', $attrs);
}

function allspice_mediavine_settings_element(): string {
    if (!allspice_member_is_ad_free()) return '';
    if (empty(allspice_ad_free_networks()['mediavine'])) return '';
    $html = '<div id="mediavine-settings"';
    foreach (allspice_mediavine_settings_attributes() as $name => $value) {
        /* Attribute names come from our own fixed table (plus filter); sanitize both sides. */
        $name = preg_replace('/[^a-z0-9\-]/', '', strtolower((string)$name));
        if ($name === '') continue;
        $html .= ' ' . $name . '="' . esc_attr((string)$value) . '"';
    }
    return $html . '></div>';
}

/*
 * Emit early in <body> (wp_body_open) so the element exists before Mediavine's script
 * initializes its slots, with a wp_footer fallback for the rare theme that never calls
 * wp_body_open. The flag guarantees exactly one element.
 */
add_action('wp_body_open', 'allspice_member_print_mediavine_settings', 1);
add_action('wp_footer', 'allspice_member_print_mediavine_settings', 1);
function allspice_member_print_mediavine_settings(): void {
    static $printed = false;
    if ($printed) return;
    $el = allspice_mediavine_settings_element();
    if ($el === '') return;
    $printed = true;
    echo $el . "\n";
}